package utils

import (
	"crypto/md5"
	"fmt"
	"sort"
	"strings"

	"go.uber.org/zap"
)

func CheckMD5Sign(queries map[string]string, key string, logContext *zap.Logger) (bool, error) {
	if len(queries["sign"]) == 0 {
		return false, &MessageError{message: "未找到参数 sign，无法完成签名检查"}
	}
	verifySign := queries["sign"]
	logContext.Info(fmt.Sprintf("verifySign => %s", verifySign))
	if len(verifySign) == 0 {
		return false, &MessageError{message: "未找到参数 sign，无法完成签名检查"}
	}

	paramsToCheck := []string{"mch_id", "biz_body", "method"}
	sort.Strings(paramsToCheck)
	kvMap := make(map[string]string)
	for _, p := range paramsToCheck {
		if SliceIndex(len(paramsToCheck), func(i int) bool {
			return paramsToCheck[i] == p
		}) == -1 {
			continue
		}
		if len(queries[p]) == 0 {
			continue
		}
		kvMap[p] = queries[p]
	}

	var kvPairToSign []string
	for k, v := range kvMap {
		kvPairToSign = append(kvPairToSign, fmt.Sprintf("%s=%s", k, v))
	}

	rawSignStringNoKey := strings.Join(kvPairToSign, "&")
	rawSignStringWithKey := rawSignStringNoKey + "&key=" + key

	logContext.Info(fmt.Sprintf("rawSignStringNoKey => %s, verifySign => %s", rawSignStringNoKey, verifySign))

	sign := fmt.Sprintf("%x", md5.Sum([]byte(rawSignStringWithKey)))
	if sign != verifySign {
		logContext.Warn(fmt.Sprintf("[验签失败]rawSignStringWithKey => %s | (%s != %s)", rawSignStringWithKey, sign, verifySign))
		return false, &MessageError{message: "验签失败"}
	}

	return true, nil
}

// 按指定的函数进行切片查找，未找到返回 -1，找到返回元素所在的索引下标
func SliceIndex(limit int, predicate func(i int) bool) int {
	for i := 0; i < limit; i++ {
		if predicate(i) {
			return i
		}
	}
	return -1
}
